package org.dromara.common.core.config;

import cn.hutool.core.util.ArrayUtil;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.SpringUtils;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.core.task.VirtualThreadTaskExecutor;
import org.springframework.scheduling.annotation.AsyncConfigurer;

import java.util.Arrays;
import java.util.concurrent.Executor;


/**
 * @Author 刘武贵
 * @Date 2024/8/15 22:00
 * @Description 功能描述：异步配置
 * 如果未使用虚拟线程则生效
 */
@AutoConfiguration
/**
 * 异步配置类，用于配置异步任务相关设置
 * 通过实现AsyncConfigurer接口，可以定制化异步任务的执行策略
 */
public class AsyncConfig implements AsyncConfigurer {

    /**
     * 自定义 @Async 注解使用系统线程池
     * 此方法根据当前环境决定返回虚拟线程执行器还是已存在的调度执行器
     * 如果应用运行在虚拟模式下，将创建并返回一个虚拟线程执行器
     * 否则，将从Spring容器中获取已配置的调度执行器
     *
     * @return Executor 返回一个异步执行器实例，可能是虚拟线程执行器或调度执行器
     */
    @Override
    public Executor getAsyncExecutor() {
        // 判断应用是否运行在虚拟模式下
        if(SpringUtils.isVirtual()) {
            // 如果是，创建并返回一个虚拟线程执行器
            return new VirtualThreadTaskExecutor("async-");
        }
        // 如果不是，从Spring容器中获取调度执行器并返回
        return SpringUtils.getBean("scheduledExecutorService");
    }

    /**
     * 异步执行异常处理
     * 获取异步未捕获异常处理程序
     * 该方法返回一个Lambda表达式，用于处理在异步操作中未被捕获的异常
     * 处理程序的主要功能是打印异常堆栈跟踪，构造并抛出一个包含异常信息的自定义异常
     *
     * @return 返回一个异步未捕获异常处理程序，该处理程序能够在发生未捕获异常时进行处理
     */
    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return (throwable, method, objects) -> {
            throwable.printStackTrace();
            StringBuilder sb = new StringBuilder();
            // 首先添加异常消息
            sb.append("Exception message - ").append(throwable.getMessage())
                .append(", Method name - ").append(method.getName());
            // 如果有参数对象，则添加参数值信息
            if (ArrayUtil.isNotEmpty(objects)) {
                sb.append(", Parameter value - ").append(Arrays.toString(objects));
            }
            throw new ServiceException(sb.toString());
        };
    }

}
